using Microsoft.Extensions.Logging;
using System;
using System.Data.Common;
using System.Text;

namespace Lzfy_His_Service.Utilities
{
    /// <summary>
    /// 数据库异常日志记录工具类
    /// </summary>
    public static class DatabaseExceptionLogger
    {
        /// <summary>
        /// 记录数据库操作异常的详细信息
        /// </summary>
        public static void LogDatabaseException(ILogger logger, Exception ex, string operation, object? parameters = null)
        {
            var logMessage = BuildDatabaseExceptionMessage(ex, operation, parameters);
            
            if (IsCriticalDatabaseError(ex))
            {
                logger.LogCritical(ex, logMessage);
            }
            else if (IsConnectionError(ex))
            {
                logger.LogError(ex, logMessage);
            }
            else
            {
                logger.LogWarning(ex, logMessage);
            }
        }

        /// <summary>
        /// 构建数据库异常详细信息
        /// </summary>
        private static string BuildDatabaseExceptionMessage(Exception ex, string operation, object? parameters = null)
        {
            var message = new StringBuilder();
            message.AppendLine($"数据库操作异常: {operation}");
            message.AppendLine($"异常类型: {ex.GetType().FullName}");
            message.AppendLine($"异常消息: {ex.Message}");
            message.AppendLine($"堆栈跟踪: {ex.StackTrace}");

            if (ex.InnerException != null)
            {
                message.AppendLine($"内部异常: {ex.InnerException.GetType().FullName}");
                message.AppendLine($"内部异常消息: {ex.InnerException.Message}");
            }

            // 添加数据库特定的错误信息
            switch (ex)
            {
                case Microsoft.Data.SqlClient.SqlException sqlEx:
                    message.AppendLine($"SQL Server 错误号: {sqlEx.Number}");
                    message.AppendLine($"SQL Server 状态: {sqlEx.State}");
                    message.AppendLine($"SQL Server 类别: {sqlEx.Class}");
                    message.AppendLine($"SQL Server 服务器: {sqlEx.Server}");
                    message.AppendLine($"SQL Server 过程: {sqlEx.Procedure}");
                    message.AppendLine($"SQL Server 行号: {sqlEx.LineNumber}");
                    message.AppendLine($"SQL Server 错误数量: {sqlEx.Errors.Count}");
                    
                    for (int i = 0; i < sqlEx.Errors.Count; i++)
                    {
                        var error = sqlEx.Errors[i];
                        message.AppendLine($"错误 {i + 1}: 错误号={error.Number}, 消息={error.Message}, 过程={error.Procedure}, 行号={error.LineNumber}");
                    }
                    break;

                case Oracle.ManagedDataAccess.Client.OracleException oracleEx:
                    message.AppendLine($"Oracle 错误号: {oracleEx.Number}");
                    message.AppendLine($"Oracle 数据源: {oracleEx.DataSource}");
                    message.AppendLine($"Oracle 过程: {oracleEx.Procedure}");
                    message.AppendLine($"Oracle 错误数量: {oracleEx.Errors.Count}");
                    
                    for (int i = 0; i < oracleEx.Errors.Count; i++)
                    {
                        var error = oracleEx.Errors[i];
                        message.AppendLine($"错误 {i + 1}: 错误号={error.Number}, 消息={error.Message}");
                    }
                    break;

                case TimeoutException timeoutEx:
                    message.AppendLine("操作超时");
                    break;

                case DbException dbEx:
                    message.AppendLine($"通用数据库错误码: {dbEx.ErrorCode}");
                    break;
            }

            // 添加参数信息（如果提供）
            if (parameters != null)
            {
                message.AppendLine($"操作参数: {System.Text.Json.JsonSerializer.Serialize(parameters)}");
            }

            return message.ToString();
        }

        /// <summary>
        /// 判断是否为关键数据库错误
        /// </summary>
        private static bool IsCriticalDatabaseError(Exception ex)
        {
            return ex switch
            {
                Microsoft.Data.SqlClient.SqlException sqlEx => sqlEx.Number switch
                {
                    2 or 53 or 258 or 10060 or 11001 => true, // 连接错误
                    18456 or 4060 => true, // 认证/权限错误
                    1205 => true, // 死锁
                    _ => false
                },
                Oracle.ManagedDataAccess.Client.OracleException oracleEx => oracleEx.Number switch
                {
                    1017 or 1031 or 12154 or 12170 or 12514 or 12535 or 12541 or 12543 or 12560 => true,
                    _ => false
                },
                _ => false
            };
        }

        /// <summary>
        /// 判断是否为连接错误
        /// </summary>
        private static bool IsConnectionError(Exception ex)
        {
            return ex switch
            {
                Microsoft.Data.SqlClient.SqlException sqlEx => sqlEx.Number switch
                {
                    2 or 53 or 258 or 10060 or 11001 or 1326 or 233 or 10054 or 10061 => true,
                    _ => false
                },
                Oracle.ManagedDataAccess.Client.OracleException oracleEx => oracleEx.Number switch
                {
                    12154 or 12170 or 12514 or 12535 or 12541 or 12543 or 12560 or 12571 or 12592 or 12599 => true,
                    _ => false
                },
                TimeoutException => true,
                _ => false
            };
        }

        /// <summary>
        /// 获取数据库操作性能信息
        /// </summary>
        public static void LogDatabasePerformance(ILogger logger, string operation, TimeSpan duration, int? affectedRows = null)
        {
            var logLevel = duration.TotalMilliseconds switch
            {
                > 5000 => LogLevel.Error,
                > 1000 => LogLevel.Warning,
                _ => LogLevel.Information
            };

            var message = $"数据库操作完成: {operation}, 耗时: {duration.TotalMilliseconds:F2}ms";
            if (affectedRows.HasValue)
            {
                message += $", 影响行数: {affectedRows.Value}";
            }

            logger.Log(logLevel, message);
        }
    }
}